﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;

namespace VerticalSpiderEditor
{
    public static class XmlDocumentTreeViewMapping
    {
        public static void BuildTreeViewByXml(XmlDocument document, TreeView treeView)
        {
            treeView.Nodes.Clear();

            foreach (XmlNode item in document.ChildNodes)
            {
                var childNode = treeView.Nodes.Add(item.Name, item.Name);
                BuildTreeViewByXml(item, childNode);
            }
        }

        public static void SelectNodeByXPath(TreeView treeView, string xpath)
        {
            var xpathItems = xpath.Split('/');

            Regex regex = new Regex("^(?<Name>[\\S]+?)\\[(?<Index>[\\d]+?)\\]$");

            TreeNode treeNode = treeView.Nodes[xpathItems[1]];

            for (int i = 2; i < xpathItems.Length; i++)
            {
                var item = xpathItems[i];
                var match = regex.Match(item);
                var index = 1;
                var name = item;
                if (match.Success)
                {
                    name = match.Groups["Name"].Value;
                    index = int.Parse(match.Groups["Index"].Value);
                }

                var list = new List<TreeNode>();
                foreach (TreeNode itemNode in treeNode.Nodes)
                {
                    if (itemNode.Name == name)
                        list.Add(itemNode);
                }

                treeNode = list.ElementAt(index - 1);
            }

            if (treeNode != null)
            {
                treeNode.TreeView.SelectedNode = treeNode;
            }
        }

        /// <summary>
        /// 只处理子集
        /// </summary>
        /// <param name="xmlNode"></param>
        /// <param name="treeNode"></param>
        public static void BuildTreeViewByXml(XmlNode xmlNode, TreeNode treeNode)
        {
            foreach (XmlNode item in xmlNode.ChildNodes)
            {

                if (item.NodeType == XmlNodeType.Comment)
                {
                    treeNode.Nodes.Add("comment", $"<!-- {item.InnerText} -->");
                }
                else if (IsAttribute(item))
                {
                    treeNode.Nodes.Add(item.Name, $"{item.Name}={item.InnerText}");
                }
                else if (item.Name == "Item" && item.ChildNodes.Count == 1 && item.ChildNodes[0].NodeType == XmlNodeType.Text)
                {
                    treeNode.Nodes.Add(item.Name, $"{item.Name}={item.InnerText}");
                }
                else if (item.Name == "Item" && item.SelectSingleNode("./Name") != null)
                {
                    var childNode = treeNode.Nodes.Add(item.Name, $"{item.Name}:{item?.SelectSingleNode("./Name")?.InnerText}");
                    BuildTreeViewByXml(item, childNode);
                }
                else
                {
                    var childNode = treeNode.Nodes.Add(item.Name, item.Name);
                    BuildTreeViewByXml(item, childNode);
                }
            }
        }

        private static bool IsAttribute(XmlNode xmlnode)
        {
            if (xmlnode.NodeType == XmlNodeType.Text)
                return true;


            string[] CommonAttributeNames = new string[]
            {
                "Type","Name", "Value"
            };

            if (CommonAttributeNames.Contains(xmlnode.Name))
                return true;

            return false;
        }

        public static string GetXPathFormTree(TreeView treeView)
        {
            var paths = new List<string>();
            GetXPathFormTreeNode(treeView.SelectedNode, paths);

            paths.Reverse();
            return "/" + string.Join("/", paths);
        }

        public static void GetXPathFormTreeNode(TreeNode treeNode, List<string> paths)
        {
            if (treeNode == null)
                return;

            if (IsList(treeNode))
            {
                var index = treeNode.Parent.Nodes.IndexOf(treeNode);

                paths.Add($"{treeNode.Name}[{index + 1}]");
                GetXPathFormTreeNode(treeNode.Parent, paths);
            }
            else
            {
                paths.Add(treeNode.Name);
                GetXPathFormTreeNode(treeNode.Parent, paths);
            }
        }

        private static bool IsList(TreeNode treeNode)
        {
            var spParentNames = new string[] { "List", "Property" };
            if (spParentNames.Contains(treeNode.Parent?.Name))
                return true;

            var basePath = GetBasePath(treeNode);
            var spPaths = new string[]
            {
                "/NetSpider/SpiderVariable/Item",
                "/NetSpider/Dependent/Item",
                "/NetSpider/Spiders/Item",
            };
            return spPaths.Contains(basePath);
        }

        private static string GetBasePath(TreeNode treeNode)
        {
            StringBuilder sb = new StringBuilder();

            while (treeNode != null)
            {
                sb.Insert(0, "/" + treeNode.Name);
                treeNode = treeNode.Parent;
            }

            return sb.ToString();
        }

        public static void SaveToDocument(XmlDocument document, string xpath, List<KeyValuePair<string, string>> kvs)
        {
            var node = document.SelectSingleNode(xpath);
            if (node == null)
                return;

            var valueNames = new string[]
            {
                "Name","Value", "Type"
            };
            if (valueNames.Contains(node.Name))
            {
                node.InnerText = kvs.FirstOrDefault().Value;
                return;
            }

            if (xpath == "/NetSpider/Dependent")
            {
                foreach (var item in kvs)
                {
                    if (item.Key != "Item")
                        throw new Exception("传入的数据不正确 " + item.Key);

                    node.AddChildNode(item.Key, item.Value);
                }

                return;
            }

            if (Regex.Match(xpath, "^/NetSpider/Dependent/\\[[\\d]+?\\]$").Success)
            {
                node.InnerText = kvs.FirstOrDefault().Value;
                return;
            }

            if (xpath == "/NetSpider/SpiderVariable")
            {
                var itemNode = node.AddChildNode("Item");
                foreach (var item in kvs)
                {
                    itemNode.AddChildNode(item.Key, item.Value);
                }
            }

            if (Regex.Match(xpath, "^/NetSpider/SpiderVariable/Item\\[[\\d]+?\\]$").Success)
            {
                foreach (var item in kvs)
                {
                    var itemNode = node.SelectSingleNode($"./{item.Key}");
                    if (itemNode == null)
                    {
                        node.AddChildNode(item.Key, item.Value);
                    }
                    else
                    {
                        itemNode.InnerText = item.Value;
                    }
                }
            }

            if (xpath == "/NetSpider/Spiders" || node.Name == "Propery")
            {
                var typeName = kvs.FirstOrDefault(n => n.Key == "Type").Value;

                if (string.IsNullOrEmpty(typeName) == false)
                {
                    node.AddChildNode("Item").AddChildNode("Type", typeName);
                }
            }

            if (node.Name == "Item")
            {
                var typeName = node.SelectSingleNode("./Type")?.InnerText;
                if (string.IsNullOrEmpty(typeName) == false)
                {
                    var type = Type.GetType(typeName);
                    if (type != null)
                    {
                        var propertyNode = node.GetOrCreateNode("Property");

                        foreach (var kvItem in kvs)
                        {
                            var property = type.GetProperty(kvItem.Key);
                            if (property == null)
                                continue;

                            var propertyItemNode = propertyNode.SelectSingleNode($"./Item[Name='{property.Name}']");
                            if (propertyItemNode == null)
                            {
                                propertyItemNode = propertyNode.AddChildNode("Item");
                                propertyItemNode.AddChildNode("Name", property.Name);
                            }

                            if (property.PropertyType.IsArray)
                            {
                                propertyItemNode.GetOrCreateNode("List");
                            }

                            if (property.PropertyType == typeof(string) || property.PropertyType.IsValueType)
                            {
                                propertyItemNode.GetOrCreateNode("Type").InnerText = TypeHelper.GetTypeFullName(property.PropertyType);
                                var itemNode = propertyItemNode.GetOrCreateNode("Value");
                                itemNode.InnerText = kvItem.Value;
                            }
                            else
                            {
                                var itemNode = propertyItemNode.GetOrCreateNode("Type");
                                itemNode.InnerText = kvItem.Value;
                            }
                        }
                    }
                }
            }

            if (node.Name == "List")
            {
                var listTypeName = node.ParentNode?.SelectSingleNode("./Type")?.InnerText;
                if (string.IsNullOrEmpty(listTypeName) == false)
                {
                    var elementType = Type.GetType(listTypeName)?.GetElementType();
                    if (elementType != null)
                    {

                        if (elementType == typeof(string) || elementType.IsValueType)
                        {
                            foreach (var item in kvs)
                            {
                                var itemNode = node.AddChildNode("Item");
                                itemNode.AddChildNode("Type", TypeHelper.GetTypeFullName(elementType));
                                itemNode.AddChildNode("Value", item.Value);
                            }
                        }
                        else
                        {
                            foreach (var item in kvs)
                            {
                                var itemNode = node.AddChildNode("Item");
                                itemNode.AddChildNode("Type", item.Value);
                            }
                        }
                    }
                }

            }
        }

        public static void AddTypeToDocument(XmlNode itemNode, object obj)
        {
            var type = obj.GetType();
            var typeFullName = TypeHelper.GetTypeFullName(type);

            var typeNode = itemNode.GetOrCreateNode("Type");
            typeNode.InnerText = typeFullName;

            if (typeof(string).IsAssignableFrom(type))
            {
                itemNode.GetOrCreateNode("Value").InnerText = obj.ToString();
                return;
            }

            if (type.IsValueType)
            {
                itemNode.GetOrCreateNode("Value").InnerText = obj.ToString();
                return;
            }

            if (type.IsClass)
            {
                var propertyNode = itemNode.GetOrCreateNode("Property");
                var properties = type.GetProperties();
                if (properties != null)
                {
                    foreach (var itemProperty in properties)
                    {
                        var itemPropertyNode = propertyNode.AddChildNode("Item");
                        itemPropertyNode.AddChildNode("Name", itemProperty.Name);

                        var propertyObj = itemProperty.GetValue(obj);
                        AddTypeToDocument(itemPropertyNode, propertyObj);
                    }
                }
            }
        }
    }
}
